/*
 * Copyright 2009 Original Author(s)
 * 
 * This file is part of Kommando
 * 
 * Kommando is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Kommando is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with Kommando.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.kommando.core.search.ranking;

import java.util.Set;

/**
 * {@link StringScorer} implementation that gives higher scores for acronym matches:
 * <p>
 * for example <code>FF</code> gives a higher score to <code>FireFox</code>
 * 
 * @author Peter De Bruycker
 */
public class AcronymStringScorer implements StringScorer {

    public float score(String label, String input, Set<Integer> scoredIndexes) {
        return score(0, label, input, scoredIndexes);
    }

    public float score(int offset, String scoreMe, String input, Set<Integer> scoredIndexes) {
        if (input.length() == 0) {
            return 0.95f - countSpaces(scoreMe) * 0.3f;
        }

        if (input.length() > scoreMe.length()) {
            return 0.0f;
        }

        // Search for steadily smaller portions of abbreviation
        for (int length = input.length(); length > 0; length--) {
            // give precedence to case sensitive matches
            int index = scoreMe.indexOf(input.substring(0, length));
            if (index < 0) {
                index = scoreMe.toLowerCase().indexOf(input.substring(0, length).toLowerCase());
            }

            if (index >= 0) {
                scoredIndexes.add(index + offset);

                int remainingLength = scoreMe.length() - (index + length);

                // Search what is left of the string with the rest of the abbreviation
                float remainingScore = score(offset + index + length, scoreMe.substring(index + length), input
                        .substring(length), scoredIndexes);
                if (remainingScore > 0) {
                    float score = index + length;
                    // ignore skipped characters if is first letter of a word
                    if (index > 0) {// if some letters were skipped
                        if (Character.isWhitespace(scoreMe.charAt(index - 1))) {
                            for (int j = index - 2; j >= 0; j--) {
                                if (Character.isWhitespace(scoreMe.charAt(j))) {
                                    score--;
                                } else {
                                    score -= 0.15;
                                }
                            }
                        }
                        if (Character.isUpperCase(scoreMe.charAt(index))) {
                            for (int j = index - 1; j >= 0; j--) {
                                if (Character.isUpperCase(scoreMe.charAt(j))) {
                                    score--;
                                } else {
                                    score -= 0.15;
                                }
                            }
                        } else {
                            score -= index;
                        }
                    }
                    score += remainingScore * remainingLength;
                    score /= scoreMe.length();
                    return score;
                }
            }
        }
        return 0;
    }

    private int countSpaces(String text) {
        int count = 0;

        for (char c : text.toCharArray()) {
            if (Character.isWhitespace(c)) {
                count++;
            }
        }

        return count;
    }
}
